package com.boarsoft.boar.soagov.svc.action;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.boarsoft.bean.ReplyInfo;
import com.boarsoft.boar.app.AppInstBiz;
import com.boarsoft.boar.common.Constants;
import com.boarsoft.boar.entity.AppInfo;
import com.boarsoft.boar.entity.AppInst;
import com.boarsoft.boar.soagov.entity.SvcInfo;
import com.boarsoft.boar.soagov.entity.SvcInst;
import com.boarsoft.boar.soagov.svc.SvcAppBiz;
import com.boarsoft.boar.soagov.svc.SvcBiz;
import com.boarsoft.boar.soagov.svc.SvcInstBiz;
import com.boarsoft.common.Authorized;
import com.boarsoft.common.Util;
import com.boarsoft.common.dao.PagedResult;
import com.boarsoft.common.util.JsonUtil;
import com.boarsoft.rpc.core.RpcContext;
import com.boarsoft.soagov.config.BwConfig;
import com.boarsoft.soagov.config.BwConfigImpl;
import com.boarsoft.soagov.config.SlaConfig;
import com.boarsoft.soagov.config.SlaConfigImpl;
import com.boarsoft.soagov.spy.SpyData;
import com.boarsoft.soagov.spy.SvcSpy;

@RestController
@RequestMapping("/svc/inst")
public class SvcInstAction {
	private static final Logger log = LoggerFactory.getLogger(SvcInstAction.class);

	/** 服务开关与升降级 */
	private static final int ACT_UP = 1;
	/** 服务开关与升降级 */
	private static final int ACT_DOWN = 2;
	/** 服务开关与升降级 */
	private static final int ACT_MOCK = 3;
	/** 增加或修改服务限流配置 */
	private static final int ACT_LIMIT_SAVE = 4;
	/** 移除服务限流配置 */
	private static final int ACT_LIMIT_REMOVE = 5;
	/** 启用服务限流、开启监控，是否真正限流取决于流量上限 */
	private static final int ACT_LIMIT_ON = 6;
	/** 停用服务限流、关闭监控 */
	private static final int ACT_LIMIT_OFF = 7;
	/** 增加或修改服务限流配置 */
	private static final int ACT_BW_SAVE = 8;
	/** 移除服务限流配置 */
	private static final int ACT_BW_REMOVE = 9;
	/** 启用黑白名单 */
	private static final int ACT_BW_ON = 10;
	/** 停用黑白名单 */
	private static final int ACT_BW_OFF = 11;

	@Autowired
	private SvcBiz svcBiz;
	// @Autowired
	// private SvcProtocolBiz svcProtocolBiz;
	@Autowired
	private SvcInstBiz svcInstBiz;
	@Autowired
	private AppInstBiz appInstBiz;
	@Autowired
	private SvcAppBiz svcAppBiz;

	@Autowired(required = false)
	private SvcSpy svcSpy;

	/**
	 * 分页查询指定服务的实例
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	@RequestMapping("/list.do")
	@Authorized(code = "svc.inst.list")
	public ReplyInfo<Object> list(String svcId, String env, String key, String orderBy, int pageNo, int pageSize) {
		SvcInfo svc = svcBiz.get(svcId);
		// SvcSpy svcSpy = applicationContext.getBean(svc.getSpy());
		String sk = svc.getUrl();
		PagedResult<SvcInst> pr = svcInstBiz.list(svcId, env, key, orderBy, pageNo, pageSize);
		// 调取当前可获取到的节点数据，并与数据表中的结果进行合并
		Map<String, Object> rm = null;
		if (Util.strIsEmpty(sk)) {
			rm = new HashMap<String, Object>();
		} else {
			rm = (Map<String, Object>) svcSpy.getData(sk);
		}
		List<SvcInst> lt = pr.getList();
		// 更新已有服务实例记录的状态（ 如果状态有变化）
		for (SvcInst x : lt) {
			String a = x.getAddr();
			Object o = rm.remove(a);
			if (o != null && o instanceof SpyData) {
				SpyData d = (SpyData) o;
				x.setSpyData(d);
				if (x.getStatus() != d.getStatus()) {
					x.setStatus(d.getStatus());
					svcInstBiz.update(x);
				}
			} else {
				log.error("Remote node {} return {}", a, o);
				x.setStatus(SvcInst.STATUS_DOWN);
			}
		}
		// 登记尚未记录的实例
		for (Entry<String, Object> en : rm.entrySet()) {
			String addr = en.getKey();
			Object o = en.getValue();
			if (o != null && o instanceof SpyData) {
				String[] a = addr.split(":");
				String ip = a[0];
				int port = Integer.parseInt(a[1]);
				AppInst n = appInstBiz.find(ip, port);
				if (n == null) {
					log.error("Unknown app instance {}", addr);
					continue;
				}
				//
				SvcInst x = new SvcInst();
				x.setCode(sk);
				x.setEnv(n.getEnv());
				x.setIp(ip);
				x.setPort(port);
				x.setAppInstId(n.getId());
				x.setAppId(n.getAppId());
				x.setServerId(n.getServerId());
				//
				SpyData d = (SpyData) o;
				x.setSpyData(d);
				x.setStatus(d.getStatus());
				x.setSvcId(svcId);
				svcInstBiz.save(x);
				lt.add(x);
			} else {
				log.error("Remote node {} return {}", addr, o);
			}
		}
		pr.setTotal(lt.size());
		return new ReplyInfo<Object>(true, pr);
	}

	@RequestMapping("/delete.do")
	@Authorized(code = "svc.inst.delete")
	public ReplyInfo<Object> delete(String id) {
		svcInstBiz.delete(id);
		return ReplyInfo.SUCCESS;
	}

	@RequestMapping("/get.do")
	@Authorized(code = "svc.inst.get")
	public ReplyInfo<String> get(String id) {
		SvcInst o = svcInstBiz.get(id);
		return new ReplyInfo<String>(true, JsonUtil.from(o));
	}

	@RequestMapping("/toggle.do")
	@Authorized(code = "svc.inst.toggle")
	public ReplyInfo<String> toggle(int act, String id, String svcId, String env, Integer status, String mockType,
			String mockJson, String key, int tpsLimit) {
		if (act < 1 || act > 11) {
			return new ReplyInfo<String>(Constants.WARN_INVALID, "act");
		}
		Map<String, Boolean> rm = new HashMap<String, Boolean>();
		// 如果没指定地址，则表示当前服务的所有实例都要升降级
		if (Util.strIsEmpty(id)) {
			SvcInfo svc = svcBiz.get(svcId);
			String sk = svc.getUrl();
			// SvcInst表的数据不可靠，直接查询暴露此服务的应用
			List<AppInfo> appLt = svcAppBiz.list(svcId);
			for (AppInfo app : appLt) {
				String appId = app.getId();
				// 查询这些应用在使用中的实例
				List<AppInst> instLt = appInstBiz.list(appId, env, null, SvcInfo.STATUS_INUSE);
				for (AppInst ai : instLt) {
					SvcInst si = svcInstBiz.find(svcId, ai.getIp(), ai.getPort());
					if (this.toggle(ai.getAddr(), sk, act, mockType, mockJson, key, tpsLimit, status)) {
						if (si == null) {
							si = new SvcInst();
							si.setAppInstId(ai.getId());
							si.setCode(svc.getUrl());
							si.setEnv(ai.getEnv());
							si.setIp(ai.getIp());
							si.setPort(ai.getPort());
							si.setStatus(status > 0 ? status : SvcInst.STATUS_ON);
							si.setSvcId(svcId);
							svcInstBiz.save(si);
						} else if (status > 0) {
							si.setStatus(status);
							svcInstBiz.update(si);
						}
						rm.put(ai.getAddr(), true);
					} else {
						rm.put(ai.getAddr(), false);
					}
				}
			}
		} else {
			String[] ka = id.split(",");
			for (String k : ka) {
				SvcInst si = svcInstBiz.get(k);
				String sk = si.getCode();
				if (this.toggle(si.getAddr(), sk, act, mockType, mockJson, key, tpsLimit, status)) {
					if (status > 0) {
						si.setStatus(status);
						svcInstBiz.update(si);
					}
				}
			}
		}
		return new ReplyInfo<String>(true, JsonUtil.from(rm));
	}

	private boolean toggle(String addr, String code, int act, String mockType, String mockJson, String key, int tpsLimit,
			Integer status) {
		// 对升级和降级做特殊处理
		switch (act) {
		case ACT_UP: // 广播通知所有节点恢复此服务提供者
			svcSpy.enableProvider(addr, code);
			break;
		case ACT_DOWN: // 广播通知所有节点暂时禁用此服务提供者
			svcSpy.disableProvider(addr, code);
			break;
		}
		//
		RpcContext.specify2(addr);
		try {
			switch (act) {
			case ACT_UP:
				return svcSpy.up(code);
			case ACT_MOCK:
				if (Util.strIsEmpty(mockType) || Util.strIsEmpty(mockJson)) {
					return svcSpy.mock(code);
				} else {
					try {
						return svcSpy.mock(code, mockType, mockJson);
					} catch (ClassNotFoundException e) {
						log.error("Mock type or json is invalid", e);
						return false;
					}
				}
			case ACT_DOWN:
				return svcSpy.down(code);
			case ACT_LIMIT_SAVE:
				if (Util.strIsEmpty(key)) {
					return svcSpy.limit(code, tpsLimit);
				} else {
					SlaConfig sc = new SlaConfigImpl(key);
					return svcSpy.limit(code, sc, tpsLimit);
				}
			case ACT_LIMIT_REMOVE:
				if (Util.strIsEmpty(key)) {
					return svcSpy.unlimit(code);
				} else {
					return svcSpy.unlimit(code, key);
				}
			case ACT_LIMIT_ON:
				return svcSpy.setSlaConfigOn(code, true);
			case ACT_LIMIT_OFF:
				return svcSpy.setSlaConfigOn(code, false);
			case ACT_BW_SAVE:
				BwConfig sc = new BwConfigImpl(key, status == 0);
				return svcSpy.addBwConfig(code, sc);
			case ACT_BW_REMOVE:
				return svcSpy.addBwConfig(code, new BwConfigImpl(key));
			case ACT_BW_ON:
				return svcSpy.setBwConfigOn(code, true);
			case ACT_BW_OFF:
				return svcSpy.setBwConfigOn(code, false);
			}
			return false;
		} catch (Exception e) {
			log.error("Error on toggle remote service {} at {}", code, addr, e);
			return false;
		} finally {
			RpcContext.specify2(null);
		}
	}
}